import { ECSErr } from "../err/ECSErr";
import { ArrUtil } from "../util/ArrUtil";
import { Bits } from "../util/Bits";
import { Cls } from "../util/Defined";
import { KConst } from "../util/KConst";
import { MapUtil } from "../util/MapUtil";
import { TypeUtil } from "../util/TypeUtil";
import { ECSCmp } from "./ECSCmp";
import { ECSEntity } from "./ECSEntity";

/**
 * [ECS]实体组件管理
 */
export class ECSCmpMgr {
	/**
	 * 维护组件类型的唯一标识
	 * 通过该标识去设置实体的组件bits 方便组件过滤器去比对筛选
	 */
	protected static _idxMap = new Map<Cls<ECSCmp>, number>();

	/**
	 * 获取组件类型的idx值
	 * @param cmp
	 */
	static getCmpIdx(cmp: Cls<ECSCmp>) {
		if (this._idxMap.has(cmp)) {
			return this._idxMap.get(cmp);
		}
		const idx = this._idxMap.size;
		this._idxMap.set(cmp, idx);
		return idx;
	}

	/** 已启用的组件类型 bits  */
	readonly cmpBits = new Bits();
	/** 所有组件Set */
	protected _cmps = new Set<ECSCmp>();
	/** 所有组件Map  */
	protected _cmpsMap = new Map<Cls<ECSCmp>, ECSCmp[]>();
	/** 所有禁用的组件map */
	protected _disableCmpsMap = new Map<Cls<ECSCmp>, Set<ECSCmp>>();
	/** 等待添加的组件 */
	protected _waitAddCmps = new Set<ECSCmp>();
	/** 等待移除的组件 */
	protected _waitRemoveCmps = new Set<ECSCmp>();
	/** 等待清理的组件 */
	protected _waitFreeCmps = new Set<ECSCmp>();
	/** 等待启用的组件 */
	protected _waitEnableCmps = new Set<ECSCmp>();
	/** 等待禁用的组件 */
	protected _waitDisableCmps = new Set<ECSCmp>();
	/** 组件有添加变化 */
	protected _addDirty = true;
	/** 组件有移除变化 */
	protected _removeDirty = true;

	constructor(
		/** 实体 */
		readonly entity: ECSEntity
	) {}

	/** 实体所在场景 */
	get scene() {
		return this.entity.scene;
	}

	/**
	 * 是否存在对应组件
	 * @param cmpType 组件类型
	 */
	has(cmpType: Cls<ECSCmp>) {
		return this._cmpsMap.has(cmpType);
	}

	/**
	 * 获取对应组件类型的第一个组件
	 * @param cmpType 组件类型
	 * @param checkAdd 只获取已执行了onAdd的组件
	 */
	get<T extends ECSCmp>(cmpType: Cls<T>, checkAdd = false): T {
		return this.getAll(cmpType, checkAdd)[0];
	}

	/**
	 * 获取对应组件类型的所有组件
	 * @param cmpType 组件类型
	 * @param checkAdd 只获取已执行了onAdd的组件
	 */
	getAll<T extends ECSCmp>(cmpType: Cls<T>, checkAdd = false): readonly T[] {
		const cmps = this._cmpsMap.get(cmpType) || KConst.EMPTY_ARR;
		if (checkAdd) {
			return cmps.filter(cmp => !this._disableCmpsMap.get(cmpType)?.has(cmp));
		}
		return cmps;
	}

	/**
	 * 添加组件
	 * @param cmp 组件
	 */
	add<T extends ECSCmp>(cmp: T): T {
		if (this._cmps.has(cmp)) {
			this.scene.logger.error(new ECSErr("实体重复添加同个组件"));
			return cmp;
		}

		if (cmp.entity) {
			this.scene.logger.error(new ECSErr("试图添加已有实体的组件"));
			return cmp;
		}

		this._addDirty = true;
		const cmpType = TypeUtil.getCls(cmp);
		this._cmps.add(cmp);
		MapUtil.addToArrMap(this._cmpsMap, cmpType, cmp);

		this._waitAddCmps.add(cmp);
		if (cmp._enable) {
			this._waitEnableCmps.add(cmp);
		} else {
			let cmps = this._disableCmpsMap.get(cmpType);
			if (!cmps) {
				cmps = new Set();
				this._disableCmpsMap.set(cmpType, cmps);
			}
			cmps.add(cmp);
		}

		cmp.entityId = this.entity.id;
		cmp.entity = this.entity;
		return cmp;
	}

	/**
	 * 移除组件
	 * @param cmp 组件
	 */
	remove(cmp: ECSCmp) {
		if (!this._cmps.has(cmp)) {
			this.scene.logger.error(new ECSErr("试图移除实体不存在自身的组件"));
			return false;
		}

		this._removeDirty = true;
		this._waitRemoveCmps.add(cmp);
		this._waitDisableCmps.delete(cmp);
		return true;
	}

	/**
	 * 移除组件类型
	 * @param cmpType
	 */
	removeType(cmpType: Cls<ECSCmp>) {
		this.getAll(cmpType).forEach(cmp => {
			this.remove(cmp);
		});
	}

	/**
	 * 实体被移除时
	 * 移除所有组件
	 */
	clear() {
		this.cmpBits.reset();
		this._cmpsMap.clear();
		this._disableCmpsMap.clear();
		this._waitAddCmps.clear();
		this._waitRemoveCmps.clear();
		this._waitEnableCmps.clear();
		this._waitDisableCmps.clear();
		this._freeCmps(this._cmps);
		this._freeCmps(this._waitFreeCmps);
	}

	private _freeCmps(cmps: Set<ECSCmp>) {
		if (cmps.size > 0) {
			cmps.forEach(cmp => this._freeCmp(cmp));
			cmps.clear();
		}
	}

	/**
	 * 启用组件
	 * @param cmp 组件
	 */
	enable(cmp: ECSCmp) {
		if (cmp._enable) return;
		if (cmp.entity !== this.entity) {
			this.scene.logger.error(new ECSErr("试图启用非自身组件"));
			return;
		}
		this._addDirty = true;
		cmp._enable = true;
		this._waitEnableCmps.add(cmp);
		this._waitDisableCmps.delete(cmp);
	}

	/**
	 * 禁用组件
	 * @param cmp 组件
	 */
	disable(cmp: ECSCmp) {
		if (!cmp._enable) return;
		if (cmp.entity !== this.entity) {
			this.scene.logger.error(new ECSErr("试图禁用非自身组件"));
			return;
		}
		this._removeDirty = true;
		cmp._enable = false;
		if (!this._waitRemoveCmps.has(cmp)) {
			this._waitDisableCmps.add(cmp);
		}
		this._waitEnableCmps.delete(cmp);
	}

	/**
	 * 更新组件添加
	 */
	updateAdd() {
		if (this._addDirty) {
			this._addDirty = false;

			//组件添加
			if (this._waitAddCmps.size > 0) {
				this._waitAddCmps.forEach(cmp => {
					cmp.doAdd();
				});
				this._waitAddCmps.clear();
			}

			//组件启用
			if (this._waitEnableCmps.size > 0) {
				this._waitEnableCmps.forEach(cmp => {
					const cmpType = TypeUtil.getCls(cmp);
					this._disableCmpsMap.get(cmpType)?.delete(cmp);
					//将组件类型对应bit设为true
					this.cmpBits.setBit(ECSCmpMgr.getCmpIdx(cmpType), true);
					//通知过滤器组件添加
					this.scene.filterMgr.onCmpAdd(cmp);
				});
				this._waitEnableCmps.clear();
			}
		}
	}

	/**
	 * 更新组件移除
	 */
	updateRemove() {
		if (this._removeDirty) {
			this._removeDirty = false;

			//组件禁用
			if (this._waitDisableCmps.size > 0) {
				this._waitDisableCmps.forEach(cmp => {
					const cmpType = TypeUtil.getCls(cmp);
					let cmps = this._disableCmpsMap.get(cmpType);
					if (!cmps) {
						cmps = new Set();
						this._disableCmpsMap.set(cmpType, cmps);
					}
					cmps.add(cmp);

					if (this._cmpsMap.get(cmpType).length === cmps.size) {
						//所有该类型组件都为禁用状态 将对应bit设为false
						this.cmpBits.setBit(ECSCmpMgr.getCmpIdx(cmpType), false);
					}
					//通知过滤器组件移除
					this.scene.filterMgr.onCmpRemove(cmp);
				});
				this._waitDisableCmps.clear();
			}

			//组件移除
			if (this._waitRemoveCmps.size > 0) {
				this._waitRemoveCmps.forEach(cmp => {
					this._cmps.delete(cmp);
					const cmpType = TypeUtil.getCls(cmp);
					const cmps = this._cmpsMap.get(cmpType);
					ArrUtil.removeFast(cmps, cmp);
					const disableCmps = this._disableCmpsMap.get(cmpType);
					disableCmps?.delete(cmp);
					this._waitFreeCmps.add(cmp);

					if (cmps.length === 0) {
						this._cmpsMap.delete(cmpType);
						this.cmpBits.setBit(ECSCmpMgr.getCmpIdx(cmpType), false);
					} else if (disableCmps && cmps.length === disableCmps.size) {
						//所有该类型组件都为禁用状态 将对应bit设为false
						this.cmpBits.setBit(ECSCmpMgr.getCmpIdx(cmpType), false);
					}
					//通知过滤器组件移除
					this.scene.filterMgr.onCmpRemove(cmp);
				});
				this._waitRemoveCmps.clear();
			}
		}
	}

	/**
	 * 更新组件清理/回收
	 */
	updateFree() {
		this._freeCmps(this._waitFreeCmps);
	}

	/**
	 * 真正清理/回收组件
	 * @param cmp 组件
	 */
	private _freeCmp(cmp: ECSCmp) {
		if (cmp.pool) {
			cmp.pool.free(cmp);
		} else {
			cmp.onFree();
		}
		cmp.entity = null;
	}
}
